package com.ozdev.utils;

import java.io.Serializable;


/**
 * Esta clase contiene metodos que prestan utilidades para string, que no estan 
 * presentes en la api de java 1.5.0_06.
 * 
 * @version 3.0: 23/05/11
 * @author Luis Gonzalez Cabezas.
 */
public class StringUtilities implements Serializable{

	/**	serialVersionUID para que la clase sea un java bean */
	private static final long serialVersionUID = 1L;
	
	private static final int PAD_LIMIT = 8192;
	
	public static final String RIGHT  = "RIGHT";	
	public static final String LEFT = "LEFT";
	
	private static final String [] PADDING = new String [Character.MAX_VALUE];
		
	static {
		  // space padding is most common, start with 64 chars
		  PADDING[32] = " ";
	  }

	/**
	* Retorna el string pasado como primer argumento ajustado al largo indicado
	* en el segundo parametro. Si el string es mas corto que el largo 
	* indicado, se rellena por la izquierda con el caracter indicado en el tercer
	* parametro.
	* 
	* @param str: string a limitar.
	* @param size: largo al cual se debe ajustar.
	* @param padChar: caracter para relleno.
	* @return
	*/
	public static String leftPad(String str, int size, char padChar) {
		if (str == null) {
			return null;
		}
		int pads = size - str.length();
		if (pads <= 0) {
			return str; // returns original String when possible
		}
		if (pads > PAD_LIMIT) {
			return leftPad(str, size, String.valueOf(padChar));
		}
		return padding(pads, padChar).concat(str);
	}
	
	/**
	* Retorna el int pasado como primer argumento ajustado al largo indicado
	* en el segundo parametro. Si el string es mas corto que el largo 
	* indicado, se rellena por la izquierda con el caracter indicado en el tercer
	* parametro.
	* 
	* @param str: int a limitar.
	* @param size: largo al cual se debe ajustar.
	* @param padChar: caracter para relleno.
	* @return
	*/
	public static String leftPad(int entero, int size, char padChar) {
		String str = String.valueOf(entero);		
		if (str == null) {
			return null;
		}
		int pads = size - str.length();
		if (pads <= 0) {
			return str; // returns original String when possible
		}
		if (pads > PAD_LIMIT) {
			return leftPad(str, size, String.valueOf(padChar));
		}
		return padding(pads, padChar).concat(str);
	}
	
	/**
	* Retorna el long pasado como primer argumento ajustado al largo indicado
	* en el segundo parametro. Si el string es mas corto que el largo 
	* indicado, se rellena por la izquierda con el caracter indicado en el tercer
	* parametro.
	* 
	* @param str: long a limitar.
	* @param size: largo al cual se debe ajustar.
	* @param padChar: caracter para relleno.
	* @return
	*/
	public static String leftPad(long entero, int size, char padChar) {
		String str = String.valueOf(entero);		
		if (str == null) {
			return null;
		}
		int pads = size - str.length();
		if (pads <= 0) {
			return str; // returns original String when possible
		}
		if (pads > PAD_LIMIT) {
			return leftPad(str, size, String.valueOf(padChar));
		}
		return padding(pads, padChar).concat(str);
	}

	/**
	* Retorna el string del largo indicado en el primer parametro. Relleno por la
	* izquierda con el caracter indicado en el segundo parametro.
	* 
	* @param size: largo al cual se debe ajustar.
	* @param padChar: caracter para relleno.
	* @return
	*/
	public static String leftPad(int size, char padChar) {
		String str = "";
		int pads = size - str.length();
		if (pads <= 0) {
			return str; // returns original String when possible
		}
		if (pads > PAD_LIMIT) {
			return leftPad(str, size, String.valueOf(padChar));
		}
		return padding(pads, padChar).concat(str);
	}
	
	private static String padding(int repeat, char padChar) {
		// be careful of synchronization in this method
		// we are assuming that get and set from an array index is atomic
		String pad = PADDING[padChar];
		if (pad == null) {
			pad = String.valueOf(padChar);
		}
		while (pad.length() < repeat) {
			pad = pad.concat(pad);
		}
		PADDING[padChar] = pad;
		return pad.substring(0, repeat);
	}
	
	/**
	* Retorna el string pasado como primer argumento ajustado al largo indicado
	* en el segundo parametro. Si el string es mas corto que el largo 
	* indicado, se rellena por la izquierda con el string indicado en el tercer
	* parametro.
	* 
	* @param str: string a limitar.
	* @param size: largo al cual se debe ajustar.
	* @param padStr: string para relleno.
	* @return
	 */
	public static String leftPad(String str, int size, String padStr) {
		  if (str == null) {
			  return null;
		  }
		  if (isEmpty(padStr)) {
			  padStr = " ";
		  }
		  int padLen = padStr.length();
		  int strLen = str.length();
		  int pads = size - strLen;
		  if (pads <= 0) {
			  return str; // returns original String when possible
		  }
		  if (padLen == 1 && pads <= PAD_LIMIT) {
			  return leftPad(str, size, padStr.charAt(0));
		  }	  		
		  if (pads == padLen) {
			  return padStr.concat(str);
		  } else if (pads < padLen) {
			  return padStr.substring(0, pads).concat(str);
		  } else {
			  char[] padding = new char[pads];
			  char[] padChars = padStr.toCharArray();
			  for (int i = 0; i < pads; i++) {
				  padding[i] = padChars[i % padLen];
			  }
			  return new String (padding).concat(str);
		  }
	  }
	  
	  public static boolean isEmpty(String str) {
		  return str == null || str.length() == 0;
	  }
	  
	  /**
	   * Retorna el string, indicado en el primer parametro, del largo indicado en el segundo parametro. Relleno por la
	   * desrecha con el caracter indicado en el segundo parametro.
	   * 
	   * @param str: string a manipular.
	   * @param size: largo al cual se debe ajustar.
	   * @param padChar: caracter para relleno.
	   * @return nuevo string
	   */
	  public static String rightPad(String str, int size, char padChar) {
		  if (str == null) {
			  return null;
		  }
		  int pads = size - str.length();
		  if (pads <= 0) {
			  return str; // returns original String when possible
		  }
		  if (pads > PAD_LIMIT) {
			  return rightPad(str, size, String.valueOf(padChar));
		  }
		  return str.concat(padding(pads, padChar));
	  }
	  
	  /**
	   * Retorna el string, indicado en el primer parametro, del largo indicado en el segundo parametro. Relleno por la
	   * desrecha con el string indicado en el segundo parametro.
	   * 
	   * @param str: string a manipular.
	   * @param size: largo al cual se debe ajustar.
	   * @param padStr: string para relleno.
	   * @return nuevo string
	   */	  
	  public static String rightPad(String str, int size, String padStr) {
		  if (str == null) {
			  return null;
		  }
		  if (isEmpty(padStr)) {
			  padStr = " ";
		  }
		  int padLen = padStr.length();
		  int strLen = str.length();
		  int pads = size - strLen;
		  if (pads <= 0) {
			  return str; // returns original String when possible
		  }
		  if (padLen == 1 && pads <= PAD_LIMIT) {
			  return rightPad(str, size, padStr.charAt(0));
		  }

		  if (pads == padLen) {
			  return str.concat(padStr);
		  } 
		  else if (pads < padLen) {
			  return str.concat(padStr.substring(0, pads));
		  } 
		  else {
			  char[] padding = new char[pads];
			  char[] padChars = padStr.toCharArray();
			  for (int i = 0; i < pads; i++) {
				  padding[i] = padChars[i % padLen];
			  }
			  return str.concat(new String(padding));
		  }
	  }
	
	  /**
	   * Retorna el string pasado como primer argumento restringido al largo indicado 
	   * en el segundo parametro. Si el string es mas corto que el largo 
	   * indicado, se rellena por la izquierda con el caracter indicado en el tercer
	   * parametro. Si es mas largo, se reduce al largo indicado. Tales procesos se realizan
	   * tomando como punto fijo la izquierda de la cadena entregada. Si es del mismo largo
	   * se retorna tal cual.
	   * 
	   * @param str: string a limitar.
	   * @param size: largo al cual se debe limitar.
	   * @param padChar: caracter para relleno.
	   * @return str limitado al tamaño dado.
	   */
	  public static String limitarIzquierda(String str, int size, char padChar){
		  
		  int largo = str.length();
		  if(largo < size)
			  return leftPad(str, size, padChar);
		  else if(largo > size)
			  return str.substring(0, size);
		  else
			  return str;
		  
	  }
	  
	  public static String restrict(String str, int size, char padChar, String fixedPoint){
		  
		  System.out.println("str = [" + str + "]");
		  
		  int length = str.length();
		  if(length < size && fixedPoint.equalsIgnoreCase(RIGHT)) {
			  return leftPad(str, size, padChar);
		  }
		  else if(length < size && fixedPoint.equalsIgnoreCase(LEFT)) {
			  return rightPad(str, size, padChar);
		  }
		  else if(length > size && fixedPoint.equalsIgnoreCase(RIGHT)) {		  
			  return str.substring(length-size, length);
		  }
		  else if(length > size && fixedPoint.equalsIgnoreCase(LEFT)) {		  
			  return str.substring(0, size);
		  }
		  else{
			  return str;
		  }
		  
	  }
	  
	  public static String restrict(int str, int size, char padChar, String fixedPoint){
		  
		  return restrict(String.valueOf(str), size, padChar, fixedPoint);
		 		  
	  }
	  
	  /**
	   * Retorna el string pasado como primer argumento restringido al largo indicado 
	   * en el segundo parametro. Si el string es mas corto que el largo 
	   * indicado, se rellena por la izquierda con el caracter indicado en el tercer
	   * parametro. Si es mas largo, se reduce al largo indicado. Tales procesos se realizan
	   * tomando como punto fijo la derecha de la cadena entregada. Si es del mismo largo
	   * se retorna tal cual.
	   * 
	   * @param str: string a limitar.
	   * @param size: largo al cual se debe limitar.
	   * @param padChar: caracter para relleno.
	   * @return str limitado al tamaño dado.
	   */
	  public static String limitarDerecha(String str, int size, char padChar){
		  
		  int largo = str.length();
		  if(largo < size)
			  return leftPad(str, size, padChar);
		  else if(largo > size)		  
			  return str.substring(largo-size, largo);
		  else
			  return str;
		  
	  }
	  
	  /**
	   * Retorna el int pasado como primer argumento restringido al largo indicado 
	   * en el segundo parametro. Si el string es mas corto que el largo 
	   * indicado, se rellena por la izquierda con el caracter indicado en el tercer
	   * parametro. Si es mas largo, se reduce al largo indicado. Tales procesos se realizan
	   * tomando como punto fijo la derecha de la cadena entregada. Si es del mismo largo
	   * se retorna tal cual.
	   * 
	   * @param str: int a limitar.
	   * @param size: largo al cual se debe limitar.
	   * @param padChar: caracter para relleno.
	   * @return str limitado al tamaño dado.
	   */
	  public static String limitarDerecha(int entero, int size, char padChar){
		  
		  String str = String.valueOf(entero);
		  int largo = str.length();
		  if(largo < size)
			  return leftPad(str, size, padChar);
		  else if(largo > size)		  
			  return str.substring(largo-size, largo);
		  else
			  return str;
		  
	  }
	  
	  /**
	   * Este metodo establece el string, pasado como primer parametro, con la cantidad
	   * de decimales pasada como segundo parametro. Utiliza el caracter pasado como tercer
	   * parametro como separador. Ademas elimina los ceros a la izquierda del separador.
	   * 
	   * @param str: a establecer como decimal.
	   * @param decimal: cantidad de decimales
	   * @param separador: caracter separador
	   * @return str establecido como decimal.
	   */
	  public static String setDecimales(String str, int decimal, char separador){
		  
		  int number = Integer.parseInt(str);
		  str = number+"";
		  int largo = str.length();
		  if(largo-decimal < 0)
			  return "0"+separador+"0";
		  else if(largo-decimal == 1)
			  return str.substring(0,1)+separador+str.substring(1);
		  else if(largo-decimal == 0)
			  return "0"+separador+str;
		  else
			  return str.substring(0, largo-decimal)+separador+str.substring(largo-decimal);
	  }
	  
	  /**
	   * Metodo sencillo que valida que la cadena dada como parametro se un valor 
	   * numerico y no un string. Retorna true si es un numero o, en su defecto, un
	   * false si no lo es. 
	   * 
	   * @param string a verificar.
	   * @return valor boleano.
	   */
	  public static boolean isNumber(String str){
			try {
				Integer.parseInt(str);
				return true;
			} catch (NumberFormatException nfe){
				return false;
			}
		}
	  
	  
	  public static String reemplazaPin(String pin){
			  
		  String nuevo = "XXXXX"+pin.substring(5,10);
		  
		  return nuevo;
			  
		  }	

	  /**
	   * Metodo que remplaza un caracter, del set de caracteres no soportados, por uno si
	   * soportado por la aplicacion. Los caracter no soportados son las vocales (minusculas 
	   * y minusculas) con tilde y la ñ. Se remplaza los caracteres con tilde por el mismo
	   * sin tilde, la ñ es remplazada por una n. 
	   * 
	   * @param cadena a corregir.
	   * @return cadena corregida.
	   */
	  public static String eliminarCaracteresNoSoportados(String cadena){
		  
		  char[] caracteresNoSoportados = {'á','é','í','ó','ú','ñ','Á','É','Í','Ó','Ú','Ñ'};
		  char[] caracteresSiSoportados = {'a','e','i','o','u','n','A','E','I','O','U','N'};
		  for(int i=0 ; i<caracteresNoSoportados.length ; i++ ){
			  cadena = cadena.replace(caracteresNoSoportados[i], caracteresSiSoportados[i]);
		  }
		  return cadena;
		  
	  }	  
	  
	 /**
	   * Metodo que remplaza un caracter, del set de caracteres indicado en el arreglo inicial, 
	   * por uno del set de caracteres indicados en el aregglo final. Ambos arreglos de caracteres
	   * debeben de tener el mismo numero de elementos, de lo contrario no podra realizarse la 
	   * conversion y se retornara el mismo string informado inicialmente.
	   * 
	   * @param cadena a corregir
	   * @param arreglo de caracteres a remplazar 
	   * @param arreglo de caracteres de remplazo
	   * @return cadena modificada
	   */
	  public static String changeCharacters(String cadena, char[] arregloInicial, char[] arregloFinal){
		  
		  if(arregloInicial.length == arregloFinal.length){
			  for(int i=0 ; i<arregloInicial.length ; i++ ){
				  cadena = cadena.replace(arregloInicial[i], arregloFinal[i]);
			  }
		  }
		  return cadena;
		  
	  }	  
	  
	  /**
	   * Metodo que remplaza el caracter indicado en el primer argumento, por el entregado en el 
	   * segundo argumento.
	   * 
	   * @param cadena a modificar
	   * @param caracter inicial a reemplazar
	   * @param caracter de remplazo
	   * @return
	   */
	  public static String changeCharacter(String cadena, char caracterInicial, char caracterRemplazo){
		  
		 cadena = cadena.replace(caracterInicial, caracterRemplazo);	
		 
		 return cadena;
		  
	  }	
	  
	  /**
	   * Metodo que hace de largo par las cadenas dadas como primer argumento. Si la cadena es de largo impar
	   * la completa con el caracter dado como segundo argumento y luego la retorna. Si es de largo par, retorna
	   * la misma cadena. Justificado a la derecha.
	   * 
	   * @param string a modificar
	   * @param caracter de relleno
	   * @return string modificado
	   */	  
	  public static String hacerLargoParDerecha(String str, char ch){
		  
		  int largo = str.length();
		  if(largo%2 == 0)
			  return str;
		  else 
			  return ch+str;
		  
	  }
	  
	  /**
	   * Metodo que hace de largo par las cadenas dadas como primer argumento. Si la cadena es de largo impar
	   * la completa con el caracter dado como segundo argumento y luego la retorna. Si es de largo par, retorna
	   * la misma cadena. Justificado a la izquierda.
	   * 
	   * @param string a modificar
	   * @param caracter de relleno
	   * @return string modificado
	   */	  
	  public static String hacerLargoParIzquierda(String str, char ch){
		  
		  int largo = str.length();
		  if(largo%2 == 0)
			  return str;
		  else 
			  return str+ch;
		  
	  }
	  
	  
		/**
		 * Metodo que convierte un numero decimal, dado como argumento, a una cadena que 
		 * representa su valor en hexadecimal y lo retorna.
		 * 
		 * @param numero a convertir
		 * @return string headecimal de largo 16.
		 */
		public static String decimalToHexadecimal(int numero) {    	  	
	       	return Integer.toHexString(numero);
	    }
		
		/**
		 * Metodo que convierte una cadena Hexadecimal, dada como argumento, a un
		 * numero decimal y lo retorna.
		 * 
		 * @param cadena hexadecimal
		 * @return numero decimal
		 */
		public static int hexadecimalToDecimal(String cadena) {
			return Integer.parseInt(cadena,16);
	    }

		/**
		 * Metodo que crea un arreglo tan grande como para contener a los dos dos recibidos como
		 * argumentos. Luego agrega el recibido como primer argumento al principio del nuevo arreglo,
		 * para continuar despues agregando al segundo. Retorna el nuevo arreglo creado. 
		 * 
		 * @param arreglo1
		 * @param arreglo2
		 * @return nuevo arreglo
		 */
		public static byte[] concatenarByte(byte[] arreglo1, byte[] arreglo2){
			
			int largoArreglo1 = arreglo1.length;
			int largoArreglo2 = arreglo2.length;
			byte[] arregloFinal = new byte[largoArreglo1+largoArreglo2];
			
			for (int i = 0; i < largoArreglo1; i++) {
				arregloFinal[i] = arreglo1[i];
			}
			for (int i = largoArreglo1; i < largoArreglo1+largoArreglo2; i++) {
				arregloFinal[i] = arreglo2[i-largoArreglo1];
			}			
			return arregloFinal;
			
		}
		
		public static String addDigitVerifier(String string){
			
			int rut = Integer.parseInt(string);
			
	        int digito;
	        int contador;
	        int multiplo;
	        int acumulador;               
	       
	        contador = 2;
	        acumulador = 0;
	        
	        while( rut != 0){
	        	multiplo = (rut % 10) * contador;
	        	acumulador = acumulador + multiplo;
	            rut = ( rut / 10);
	            contador = contador + 1;
	            if( contador == 8 ){
	                contador = 2;   
	            }
	        }
	        
	        digito = 11 - (acumulador % 11);
	        
	        switch (digito) {
			case 1:
			case 2:
			case 3:
			case 4:
			case 5:
			case 6:
			case 7:
			case 8:
			case 9:				
				string += "-"+digito;
				break;
			case 10:
				string += "-K";
				break;
			case 11:
				string += "-0";
				break;
			default:
				break;
			}		
	        
			return string;
			
		}
		 /**
		  * Metodo que verifica que el parametro entregado corresponda a un rut. Sin puntos intermedios (mantisa+'-'+digitoVerificador).
		  * Para ello valida, primero, que la cadena no sea mas larga de 10 caracteres, maximo permitido para almacenar un rut. Luego valida
		  * que el penultimo caracter corresponda al caracter '-'. Luego valida que el substring correspondiente a la mantisa corresponda a
		  * un numero y no contenga letras.
		  * 
		  * @param str
		  * @return
		  */
		public static boolean isRut(String str){
			
			if(str.length() != 10){
				return false;
			}
			else if (str.charAt(str.length()-2) != '-') {
				return false;
			}
			else if (!isNumber(str.substring(0, str.length()-2))) {
				return false;
			}
			else
				return true;
			
		}
		
		public static String deleteCharacter(String string, char character){
			
			String newString = "";
			for (int i = 0; i < string.length(); i++) {
				if (string.charAt(i) != character) {
					newString += string.charAt(i);
				}
			}			
			return newString;
			
		}
		
		/**
		 * Crea una nueva cadena compuesta por sola las letras de la cedena pasada como parametro.
		 *  
		 * @param str a filtrar
		 * @return string compuesto solo por letras
		 */
		public static String filterOnlyLetters(String str){
			
			String response = "";
			
			for (int i = 0; i < str.length(); i++) {
				int asciiValue = (int)str.charAt(i);
				if ((asciiValue >= 65 && asciiValue <= 90) || (asciiValue >= 97 && asciiValue <= 122)) {
					response += str.charAt(i);
				}
			}
			
			return response;
			
		}
		
		public static boolean correctLength(String str, int lenght) {
			if (str.length() <= lenght) 
				return true;
			else
				return false;
		}

		//Reemplaza la palabra porciento por el signo %.
		public static String reemplazarPorcentaje(String str) {	
			return	str.replace("porciento", "%");

		}
}
